{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "    \n",
    "class LR(object):\n",
    "    \n",
    "    #决策函数为sigmoid函数\n",
    "    @staticmethod\n",
    "    def fn(w,x):\n",
    "        return 1.0/(1.0 + np.exp(x.dot(-w)))\n",
    "    \n",
    "    #交叉熵损失函数\n",
    "    @staticmethod\n",
    "    def loss(y,y_hat):\n",
    "        return np.sum(np.nan_to_num(-y * np.log(y_hat) - (1-y) * np.log(1-y_hat)))\n",
    "    \n",
    "    #交叉熵损失函数对权重w的一阶导数\n",
    "    @staticmethod\n",
    "    def grad(y,y_hat,x):\n",
    "        return (y_hat - y) * x\n",
    "    \n",
    "    \n",
    "    \n",
    "class FTRL(object):\n",
    "    \n",
    "    def __init__(self, dim, l1, l2, alpha, beta, decisionFunc=LR):\n",
    "        self.dim = dim                    #特征维度\n",
    "        self.decisionFunc = decisionFunc  #决策函数\n",
    "        self.z = np.zeros(dim)\n",
    "        self.n = np.zeros(dim)            #累积梯度平方和\n",
    "        self.w = np.zeros(dim)\n",
    "        self.l1 = l1                     #L1正则参数\n",
    "        self.l2 = l2                     #L2正则参数\n",
    "        self.alpha = alpha               #学习率参数α\n",
    "        self.beta = beta                 #学习率参数β\n",
    "        \n",
    "    def predict(self, x):\n",
    "        return self.decisionFunc.fn(self.w, x)\n",
    "    \n",
    "    \n",
    "    def update(self, x, y):\n",
    "        #更新每个维度的w\n",
    "        self.w = np.array([0 if np.abs(self.z[i]) <= self.l1 else (np.sign(\n",
    "            self.z[i]) * self.l1 - self.z[i]) / (self.l2 + (self.beta + np.sqrt(self.n[i])) / self.alpha) for i in range(self.dim)])\n",
    "        #求出sigmoid预测值y_hat(用于求梯度).\n",
    "        y_hat = self.predict(x)  \n",
    "        #损失函数梯度\n",
    "        g = self.decisionFunc.grad(y, y_hat, x)  \n",
    "        sigma = (np.sqrt(self.n + g * g) - np.sqrt(self.n)) / self.alpha\n",
    "        self.z += g - sigma * self.w\n",
    "        self.n += g * g\n",
    "        return self.decisionFunc.loss(y, y_hat)\n",
    "    \n",
    "\n",
    "    def train(self, x_train, y_train, min_loss=0.01, epochs=10,early_stop = 10,print_loss = False):\n",
    "        for n in range(epochs):\n",
    "            itr = 0\n",
    "            for i in range(x_train.shape[0]):\n",
    "                x = x_train.iloc[i]\n",
    "                y = y_train.iloc[i][0]\n",
    "                loss = self.update(x, y)\n",
    "                if i%500 == 0 and print_loss:\n",
    "                    print (\"epoch: \"+str(n)+\" i= \" + str(i) + \"\\tloss=\" + str(loss))\n",
    "                if loss < min_loss:\n",
    "                    itr += 1\n",
    "                else:\n",
    "                    itr = 0\n",
    "                if itr >= early_stop:\n",
    "                    print (\"loss have less than\", min_loss, \"  for \", itr, \"iterations,early stop!\")\n",
    "                    return loss\n",
    "        return loss\n",
    "            \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
